Tip |
Despite the fact that FIR filters may have linear phase, they may require too many computations to be implemented, and are not appropriate for some applications when hardware is limited. IIR filters can be used in these cases as they require fewer computations than FIR filters. However, IIR filters usually do not have linear phase or it is very difficult to get a linear phase. A pesar del hecho de que los filtros FIR tiene una fase lineal, estos requieren demasiadas operaciones para ser implementados en algunas aplicaciones donde el hardware es limitado. Los filtros IIR pueden ser usados en estos casos ya que estos requieres menos número de operaciones que los filtros FIR. Sin embargo, los filtros IIR usualmente no tienen una fase lineal o es muy difícil conseguir una fase lineal. |
Butterworth low pass filter |
The magnitude response of this type of filters is maximally flat in the pass band. For a continuous-time Butterworth low pass filter, magnitude-squared functions is as shown below. The poles of this type of filter that lie on the left part of the s-plane are shown in the same figure. In order to achieve a stable and causal filter, the poles on the left-part of the s-plane must be chosen. La respuesta en amplitud de este tipo de filtros es plana al máximo en la banda de paso. Para un filtro pasa bajas de Butterworth en tiempo-continuo, el cuadrado de la función de amplitud es como se muestra debajo. Los polos de este tipo de filtros que caen en la parte izquierda del plano-s se muestran en la misma figura. A fin de conseguir un filtro causal y estable, los polos en la parte izquierda del plano-s deben de escogerse. |
Problem 1 |
Show that H(s) is transformed in H(z) as shown when the bilinear transformation is used. Demostrar que H(s) se transforma en H(z) como se muestra cuando se usa la transformada bilineal. |
Problem 2 |
To transform a low-pass filter to a high-pass filter, the transformation shown below is used. Show that H(s) is transformed in H(z) as shown when this transformation is applied. Para transformar un filtro pasa-bajas a un filtro pasa-altas, la transformación mostrada debajo es usada. Demostrar que H(s) se transforma en H(z) como se muestra cuando se aplica esta transformación. |
Problem 3 |
Create a Wintempla dialog application called Butterworth to design an IIR filter using a Butterworth approximation. Use Wintempla to create the GUI shown. The Math::IIRButterworth class uses a bi-quadratic structure for the filter implementation as shown in the system function below. Cree una aplicación de dialogo de Wintempla llamada Butterworth para diseñar un filtro IIR usando una aproximación de Butterworth. Use Wintempla para crear la GUI mostrada. La clase Math::IIRButterworth usa una estructura bi-cuadrática para la implementación del filtro como se muestra en la función del sistema de abajo. |
Butterworth.h |
#pragma once //______________________________________ Butterworth.h #include "Resource.h" #define POINT_COUNT 512 #define FREQ_MAX 3.12 #define FREQ_RES 3120 #define GAIN_MIN -50.0 #define GAIN_RES 50 class Butterworth: public Win::Dialog { public: Butterworth() { } ~Butterworth() { } IIR::ButterworthFilter filter; void RefreshGraphs(); ... }; |
Butterworth.cpp |
... void Butterworth::Window_Open(Win::Event& e) { this->Move(0, 0, true); //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (radians)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX= 0.0; xyMagnitude.MaxX= M_PI; xyMagnitude.MinY= -100.0; xyMagnitude.MaxY= 0.0; xyMagnitude.Graphs.Add(POINT_COUNT); const double deltaX = M_PI /(POINT_COUNT-1); for (int i=0; i<POINT_COUNT; i++) { xyMagnitude.Graphs[0][i].x = i*deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } //________________________________________________________ xyGroupDelay xyGroupDelay.CaptionX = L"Frequency (radians)"; xyGroupDelay.CaptionY = L"Group Delay (samples)"; xyGroupDelay.MinX= 0.0; xyGroupDelay.MaxX= M_PI; xyGroupDelay.MinY= 0.0; xyGroupDelay.MaxY= 40.0; xyGroupDelay.Graphs.Add(POINT_COUNT); for (int i=0; i<POINT_COUNT; i++) { if (i == 0) { xyGroupDelay.Graphs[0][i].x = 0.000001; } else { xyGroupDelay.Graphs[0][i].x = i*deltaX; } xyGroupDelay.Graphs[0][i].y = 0.0; } //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ sldCutFreq sldCutFreq.SetRange(1, FREQ_RES); sldCutFreq.Position = FREQ_RES/2; tbxCutFreq.DoubleValue = FREQ_MAX/2.0; //________________________________________________________ sldStopFreq sldStopFreq.SetRange(1, FREQ_RES); sldStopFreq.Position = FREQ_RES/2; tbxStopFreq.DoubleValue = FREQ_MAX/2.0; //________________________________________________________ sldStopGain sldStopGain.SetRange(1, GAIN_RES); sldStopGain.Position = GAIN_RES/2; tbxStopGain.DoubleValue = GAIN_MIN/2.0; // radioLowPass.Checked = true; RefreshGraphs(); } void Butterworth::sldCutFreq_Hscroll(Win::Event& e) { const int position = sldCutFreq.HasPositionChanged(); if (position < 0) return; tbxCutFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES; RefreshGraphs(); } void Butterworth::sldStopFreq_Hscroll(Win::Event& e) { const int position = sldStopFreq.HasPositionChanged(); if (position < 0) return; tbxStopFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES; RefreshGraphs(); } void Butterworth::sldStopGain_Hscroll(Win::Event& e) { const int position = sldStopGain.HasPositionChanged(); if (position < 0) return; tbxStopGain.DoubleValue = (position*GAIN_MIN)/GAIN_RES; RefreshGraphs(); } void Butterworth::radioLowPass_Click(Win::Event& e) { RefreshGraphs(); } void Butterworth::radioHighPass_Click(Win::Event& e) { RefreshGraphs(); } void Butterworth::RefreshGraphs() { const double cutFreq = tbxCutFreq.DoubleValue; const double stopFreq = tbxStopFreq.DoubleValue; const double stopGain = tbxStopGain.DoubleValue; if (radioLowPass.Checked == true) { if (filter.CreateLowPass(cutFreq, -1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } else { if (filter.CreateHighPass(cutFreq, -1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } // double freq; double gain; int i; //___________________________________________________ Gain for (i=0; i<POINT_COUNT; i++) { freq = xyMagnitude.Graphs[0][i].x; gain = filter.biquadsCascade.GetMagnitude(freq); if (gain == 0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(gain); } } xyMagnitude.RefreshAll(); //___________________________________________________ Group Delay for (i=0; i<POINT_COUNT; i++) { freq = xyGroupDelay.Graphs[0][i].x; xyGroupDelay.Graphs[0][i].y = filter.biquadsCascade.GetGroupDelay(freq); } xyGroupDelay.AutoScaleY(false); //___________________________________________________ Poles double real1, imag1, real2, imag2; int j = 0; const int size = filter.biquadsCascade.GetSize(); polarZeroPole.Graphs[0].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquadsCascade[i].GetPoles(real1, imag1, real2, imag2); polarZeroPole.Graphs[0][j].x = atan2(imag1, real1); polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[0][j].x = atan2(imag2, real2); polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2); j++; } //___________________________________________________ Zeros j = 0; polarZeroPole.Graphs[1].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquadsCascade[i].GetZeros(real1, imag1, real2, imag2); polarZeroPole.Graphs[1][j].x = atan2(imag1, real1); polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[1][j].x = atan2(imag2, real2); polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2); j++; } polarZeroPole.Refresh(); //___________________________________________________ System Function IIR::BiquadsCascade biquadsCascade = filter.biquadsCascade; const int N = biquadsCascade.GetSize(); wstring text; wchar_t wtext[512]; for (int i = 0; i < N; i++) { _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n", biquadsCascade[i].b0, biquadsCascade[i].b1, biquadsCascade[i].b2, biquadsCascade[i].a1, biquadsCascade[i].a2); text += wtext; } tbxSystemFunction.Text = text; } |
Problem 4 |
Create a Wintempla dialog application called Chebyshev to design an IIR filter using a Chebyshev approximation. Use Wintempla to create the GUI shown. The code of this problem is very similar to the code of the previous problem; the main difference is use the Math::IIRChebyshev class instead of the Math::IIRButterworth class. Cree una aplicación de dialogo de Wintempla llamada Chebyshev para diseñar un filtro IIR usando una aproximación de Chebyshev . Use Wintempla para crear la GUI mostrada. El código de este problema es muy semejante al código del problema previo; la diferencia principal es usar la clase Math::IIRChebyshev en lugar de la clase Math::IIRButterworth. |
Chebyshev.h |
#pragma once //______________________________________ Chebyshev.h #include "Resource.h" #define POINT_COUNT 512 #define FREQ_MAX 3.12 #define FREQ_RES 3120 #define GAIN_MIN -50.0 #define GAIN_RES 50 class Chebyshev: public Win::Dialog { public: Chebyshev() { } ~Chebyshev() { } IIR::ChebyshevFilter filter; void RefreshGraphs(); ... }; |
Chebyshev.cpp |
... void Chebyshev::Window_Open(Win::Event& e) { this->Move(0, 0, true); //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (radians)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX= 0.0; xyMagnitude.MaxX= M_PI; xyMagnitude.MinY= -90.0; xyMagnitude.MaxY= 10.0; xyMagnitude.Graphs.Add(POINT_COUNT); const double deltaX = M_PI /(POINT_COUNT-1); for(int i=0; i<POINT_COUNT; i++) { xyMagnitude.Graphs[0][i].x = i*deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } //________________________________________________________ xyGroupDelay xyGroupDelay.CaptionX = L"Frequency (radians)"; xyGroupDelay.CaptionY = L"Group Delay (samples)"; xyGroupDelay.MinX= 0.0; xyGroupDelay.MaxX= M_PI; xyGroupDelay.MinY= 0.0; xyGroupDelay.MaxY= 40.0; xyGroupDelay.Graphs.Add(POINT_COUNT); for(int i=0; i<POINT_COUNT; i++) { if (i == 0) { xyGroupDelay.Graphs[0][i].x = 0.000001; } else { xyGroupDelay.Graphs[0][i].x = i*deltaX; } xyGroupDelay.Graphs[0][i].y = 0.0; } //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ sldCutFreq sldCutFreq.SetRange(1, FREQ_RES); sldCutFreq.Position = FREQ_RES/2; tbxCutFreq.DoubleValue = FREQ_MAX/2.0; //________________________________________________________ sldStopFreq sldStopFreq.SetRange(1, FREQ_RES); sldStopFreq.Position = FREQ_RES/2; tbxStopFreq.DoubleValue = FREQ_MAX/2.0; //________________________________________________________ sldStopGain sldStopGain.SetRange(1, GAIN_RES); sldStopGain.Position = GAIN_RES/2; tbxStopGain.DoubleValue = GAIN_MIN/2.0; // radioLowPass.Checked = true; RefreshGraphs(); } void Chebyshev::sldCutFreq_Hscroll(Win::Event& e) { const int position = sldCutFreq.HasPositionChanged(); if (position < 0) return; tbxCutFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES; RefreshGraphs(); } void Chebyshev::sldStopFreq_Hscroll(Win::Event& e) { const int position = sldStopFreq.HasPositionChanged(); if (position < 0) return; tbxStopFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES; RefreshGraphs(); } void Chebyshev::sldStopGain_Hscroll(Win::Event& e) { const int position = sldStopGain.HasPositionChanged(); if (position < 0) return; tbxStopGain.DoubleValue = (position*GAIN_MIN)/GAIN_RES; RefreshGraphs(); } void Chebyshev::radioLowPass_Click(Win::Event& e) { RefreshGraphs(); } void Chebyshev::radioHighPass_Click(Win::Event& e) { RefreshGraphs(); } void Chebyshev::RefreshGraphs() { const double cutFreq = tbxCutFreq.DoubleValue; //0.1*M_PI; const double stopFreq = tbxStopFreq.DoubleValue; //0.19530*M_PI; const double stopGain = tbxStopGain.DoubleValue; //-20.0; if (radioLowPass.Checked == true) { if (filter.CreateLowPass(cutFreq, 1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } else { if (filter.CreateHighPass(cutFreq, 1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } // double freq; double gain; int i; //___________________________________________________ Gain for(i=0; i<POINT_COUNT; i++) { freq = xyMagnitude.Graphs[0][i].x; gain = filter.biquadsCascade.GetMagnitude(freq); if (gain == 0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(gain); } } xyMagnitude.RefreshAll(); //___________________________________________________ Group Delay for(i=0; i<POINT_COUNT; i++) { freq = xyGroupDelay.Graphs[0][i].x; xyGroupDelay.Graphs[0][i].y = filter.biquadsCascade.GetGroupDelay(freq); } xyGroupDelay.AutoScaleY(false); //___________________________________________________ Poles double real1, imag1, real2, imag2; int j = 0; const int size = filter.biquadsCascade.GetSize(); polarZeroPole.Graphs[0].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquadsCascade[i].GetPoles(real1, imag1, real2, imag2); polarZeroPole.Graphs[0][j].x = atan2(imag1, real1); polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[0][j].x = atan2(imag2, real2); polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2); j++; } //___________________________________________________ Zeros j = 0; polarZeroPole.Graphs[1].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquadsCascade[i].GetZeros(real1, imag1, real2, imag2); polarZeroPole.Graphs[1][j].x = atan2(imag1, real1); polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[1][j].x = atan2(imag2, real2); polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2); j++; } polarZeroPole.Refresh(); //___________________________________________________ System Function IIR::BiquadsCascade biquadsCascade = filter.biquadsCascade; const int N = biquadsCascade.GetSize(); wstring text; wchar_t wtext[512]; for (int i = 0; i < N; i++) { _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n", biquadsCascade[i].b0, biquadsCascade[i].b1, biquadsCascade[i].b2, biquadsCascade[i].a1, biquadsCascade[i].a2); text += wtext; } tbxSystemFunction.Text = text; } |
Problem 5 |
Create a Wintempla dialog application called Elliptic to design an IIR filter using an Elliptic approximation. Use the IIR::EllipticFilter class. Remember that this class is designed for a bi-quadratic structure implementation and the order of the filter will always be even. Cree una aplicación de dialogo de Wintempla llamada Elliptic para diseñar un filtro IIR usando una aproximación Eliptica. Use la clase IIR::EllipticFilter. Recuerde que esta clase está diseñada para implementarse en una estructura bi-cuadrática y el orden del filtro será siempre par. |
Problem 6 |
Create a Wintempla dialog application called LinkPlay to play a wave file using two Linkwitz-Riley filters. Cree una aplicación de diálogo de Wintempla llamada LinkPlay para reproducir un archivo wave usando dos filtros Linkwitz-Riley. |
Step A |
Edit the stdafx.h file to activate the DAC and ADC by removing the comments of the shown line. Edite el archivo stdafx.h para activar el DAC y el ADC removiendo los comentarios de la línea mostrada. |
stdafx.h |
... //_________________________________________ MIDI, Audio Card DAC's and ADC's (or GDI Game for timers) //#define WIN_DAC_ADC_SUPPORT ... |
Step B |
Use Wintempla to insert a Digital to Analog Converter (DAC). Once Wintempla is open, click the Show All Controls in Toolbox to show all controls. Set the name to dacOutput. In the Events tab, be sure all events are unselected. Insert a Signal View (be sure all events are unselected). Insert a button to Play and another one to Stop as shown. Insert a Drop Down List to select the output device. Then, insert a slider (with the Hscroll event), a textbox and a label) for the frequency. Finally, insert two labels and two sliders (with the Hscroll event) for the low frequency gain and the high frequency gain as shown. Use Wintempla para inserte un Digital to Analog Converter (DAC). Una vez que Wintempla se abre, haga clic en Show All Controls in Toolbox para mostrar todos los controles. Fije el nombre a dacOutput. En la pestaña de eventos, asegúrese que todos los eventos están deseleccionados. Inserte un Signal View (asegúrese que todos los eventos están deseleccionados). Inserte un botón para Reproducir y otro para Detener como se muestra. Inserte una Drop Down List para seleccionar el dispositivo de salida. Entonces, inserte un slider (con el evento Hscroll), una caja de texto y una etiqueta. Finalmente, inserte dos etiquetas y dos sliders (con el evento Hscroll) para la ganancia de la baja frecuencia y para la ganancia para la frecuencia alta como se muestra. |
Step C |
Edit the LinkPlay.h file and the LinkPlay.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the LinkPlay class is derived from Mm::IAudioOut). Remember that an interface is used to pass a set of functions to another function or another object. Edite los archivos LinkPlay.h y LinkPlay.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase LinkPlay se deriva de Mm::IAudioOut). Recuerde que una interface es usada para pasar un conjunto de funciones a otra función u objeto. |
LinkPlay.h |
#pragma once //______________________________________ LinkPlay.h #include "Resource.h" #define FREQ_RES 100 #define FREQ_MIN 80.0 #define FREQ_MAX 14000.0 class LinkPlay: public Win::Dialog, public Mm::IAudioOut { public: LinkPlay() { samplesPerSec = 0; highGain = 1.0; lowGain = 1.0; } ~LinkPlay() { } double highGain; double lowGain; unsigned int samplesPerSec = 0; void CreateFilters(); IIR::BiquadSection lowRightFilter; IIR::BiquadSection lowLeftFilter; IIR::BiquadSection highLeftFilter; IIR::BiquadSection highRightFilter; Mm::WaveFile waveFile; //______________________________________________________________ Mm::IAudioOut void AudioOutStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution); void AudioOutData(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution, WAVEHDR* waveHdr); void AudioOutStopped(); protected: ... }; |
PlayLink.cpp |
... void LinkPlay::Window_Open(Win::Event& e) { btStop.Enabled = false; //________________________________________________________ ddDevice const int count = ::waveOutGetNumDevs(); WAVEOUTCAPS woc; const int wsize= sizeof(WAVEOUTCAPS); for (int i = 0; i < count; i++) { if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR) { ddDevice.Items.Add(woc.szPname, i); } } ddDevice.SelectedIndex = 0; //________________________________________________________ sldCutFrequency sldCutFrequency.SetRange(0, FREQ_RES); sldCutFrequency.Position = FREQ_RES/2; tbxCutFrequency.DoubleValue = FREQ_MIN+ 0.5*(FREQ_MAX - FREQ_MIN); //________________________________________________________ sldHighFreqGain sldHighFreqGain.SetRange(-50, 0); sldHighFreqGain.Position = 0; //________________________________________________________ sldLowFreqGain sldLowFreqGain.SetRange(-50, 0); sldLowFreqGain.Position = 0; } void LinkPlay::CreateFilters() { const double cutFreq_Hertz = tbxCutFrequency.DoubleValue; lowRightFilter.CreateLinkwitzRileyLowPass(cutFreq_Hertz, samplesPerSec); lowLeftFilter.CreateLinkwitzRileyLowPass(cutFreq_Hertz, samplesPerSec); highLeftFilter.CreateLinkwitzRileyHighPass(cutFreq_Hertz, samplesPerSec); highRightFilter.CreateLinkwitzRileyHighPass(cutFreq_Hertz, samplesPerSec); } void LinkPlay::sldCutFrequency_Hscroll(Win::Event& e) { const int position = sldCutFrequency.HasPositionChanged(); if (position < 0) return; tbxCutFrequency.DoubleValue = FREQ_MIN+ position*(FREQ_MAX - FREQ_MIN)/FREQ_RES; CreateFilters(); } void LinkPlay::btPlay_Click(Win::Event& e) { //________________________________________________________ 1. Prompt to the user to get the filename Win::FileDlg dlg; dlg.Clear(); dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav"); if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return; //________________________________________________________ 2. Get the Device ID LPARAM deviceID = WAVE_MAPPER; ddDevice.GetSelectedData(deviceID); //________________________________________________________ 3. Open the Wave File const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath()); if (error != NULL) { this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 4. Start the DAC error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSamplesPerSecond(), waveFile.GetNumChannels(), waveFile.GetBitsResolution(), 16384, this); if (error != NULL) { this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR); return; } } void LinkPlay::btStop_Click(Win::Event& e) { dacOutput.Stop(); } void LinkPlay::AudioOutStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution) { this->samplesPerSec = samplesPerSec; btPlay.Enabled = false; btStop.Enabled = true; ddDevice.Enabled = false; EnableCloseButton(false); CreateFilters(); } void LinkPlay::AudioOutData(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution, WAVEHDR* waveHdr) { waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength); Sys::Sample16 *samples = (Sys::Sample16 *)waveHdr->lpData; const int numSamples = (waveHdr->dwBytesRecorded)/4; // four bytes per sample double left, right; double low, high; int i; for (i = 0; i < numSamples; i++) { //________________________________________________________________ LEFT low = lowGain*lowLeftFilter.ComputeOutput(samples[i].channel_1); high = highGain*highLeftFilter.ComputeOutput(samples[i].channel_1); left = (low-high)/2.0; if (left > 32765.0) left = 32765.0; if (left < -32765.0) left = -32765.0; samples[i].channel_1 = (__int16)(left); //________________________________________________________________ RIGHT low = lowGain*lowRightFilter.ComputeOutput(samples[i].channel_2); high = highGain*highRightFilter.ComputeOutput(samples[i].channel_2); right = (low-high)/2.0; if (right > 32765.0) right = 32765.0; if (right < -32765.0) right = -32765.0; samples[i].channel_2 = (__int16)(right); } svMain.RefreshFromDAC(waveHdr, numbChannels, bitsResolution); } void LinkPlay::AudioOutStopped() { btPlay.Enabled = true; btStop.Enabled = false; ddDevice.Enabled = true; waveFile.Close(); EnableCloseButton(true); } void LinkPlay::sldHighFreqGain_Hscroll(Win::Event& e) { const int position = sldHighFreqGain.Position; highGain = pow(10.0, position/20.0); } void LinkPlay::sldLowFreqGain_Hscroll(Win::Event& e) { const int position = sldLowFreqGain.Position; lowGain = pow(10.0, position/20.0); } |
Problem 7 |
Create a Wintempla dialog application called LowPassBoost to design an IIR Shelving filter to boost bass. Cree una aplicación de dialogo de Wintempla llamada LowPassBoost para diseñar un filtro IIR Shelving para amplificar los bajos. |
LowPassBoost.h |
#pragma once //______________________________________ LowPassBoost.h #include "Resource.h" #define POINT_COUNT 512 class LowPassBoost: public Win::Window { public: LowPassBoost() { } ~LowPassBoost() { } void RefreshGraph(); const wchar_t * GetClassName(){return L"LowPassBoost";} . . . }; |
LowPassBoost.cpp |
. . . void LowPassBoost::Window_Open(Win::Event& e) { //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (Hz)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX= 0.0; xyMagnitude.MaxX= 1000.0; xyMagnitude.MinY= -25.0; xyMagnitude.MaxY= 5.0; xyMagnitude.DivisionCountY = 6; xyMagnitude.Graphs.Add(POINT_COUNT); const double deltaX = xyMagnitude.MaxX /(POINT_COUNT-1); for (int i=0; i<POINT_COUNT; i++) { xyMagnitude.Graphs[0][i].x = i*deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } //________________________________________________________ sldCutFreq sldCutFreq.SetRange(30, 200); sldCutFreq.Position = 100; tbxCutFreq.DoubleValue = sldCutFreq.Position; //________________________________________________________ sldGain sldGain.SetRange(1, 20); sldGain.Position = 6; tbxGain.DoubleValue = sldGain.Position; //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ sldQ sldQ.SetRange(100, 200); sldQ.Position = 100; tbxQ.DoubleValue = 100.0/200.0; // RefreshGraph(); } void LowPassBoost::sldCutFreq_Hscroll(Win::Event& e) { int position = 0; if (sldCutFreq.GetPosition(position) == false) return; tbxCutFreq.DoubleValue = position; RefreshGraph(); } void LowPassBoost::sldGain_Hscroll(Win::Event& e) { int position = 0; if (sldGain.GetPosition(position) == false) return; tbxGain.DoubleValue = position; RefreshGraph(); } void LowPassBoost::sldQ_Hscroll(Win::Event& e) { int position = 0; if (sldQ.GetPosition(position) == false) return; tbxQ.DoubleValue = position/200.0; RefreshGraph(); } void LowPassBoost::RefreshGraph() { const double samplingFreq = 44100.0; const double fc = tbxCutFreq.DoubleValue; const double gain = tbxGain.DoubleValue; const double Q = tbxQ.DoubleValue; IIR::BiquadSection biquad; biquad.CreateShelvingLow(fc, samplingFreq, gain, Q, true); //biquad.CreateShelvingHigh(fc, samplingFreq, gain, Q, true); IIR::PolarBiquadSection polarBiquad = biquad; double freqHz; double freqRad; double g; int i; //___________________________________________________ 1. Magnitude for (i = 0; i < POINT_COUNT; i++) { freqHz= xyMagnitude.Graphs[0][i].x; freqRad = 2.0*M_PI*freqHz/samplingFreq; g = polarBiquad.GetMagnitude(freqRad); if (g == 0.0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(g); } } xyMagnitude.RefreshAll(); //___________________________________________________ 2. Poles double real1, imag1, real2, imag2; polarZeroPole.Graphs[0].SetPointCount(2); polarBiquad.GetPoles(real1, imag1, real2, imag2); // Pole 1 polarZeroPole.Graphs[0][0].x = atan2(imag1, real1); polarZeroPole.Graphs[0][0].y = sqrt(real1*real1 + imag1*imag1); // Pole 2 polarZeroPole.Graphs[0][1].x = atan2(imag2, real2); polarZeroPole.Graphs[0][1].y = sqrt(real2*real2 + imag2*imag2); //___________________________________________________ 3. Zeros polarZeroPole.Graphs[1].SetPointCount(2); polarBiquad.GetZeros(real1, imag1, real2, imag2); // Zero 1 polarZeroPole.Graphs[1][0].x = atan2(imag1, real1); polarZeroPole.Graphs[1][0].y = sqrt(real1*real1 + imag1*imag1); // Zero 2 polarZeroPole.Graphs[1][1].x = atan2(imag2, real2); polarZeroPole.Graphs[1][1].y = sqrt(real2*real2 + imag2*imag2); // polarZeroPole.Refresh(); } |